package com.jprocms.module.cms.es.util;

import cn.hutool.core.lang.Assert;
import co.elastic.clients.elasticsearch._types.FieldSort;
import co.elastic.clients.elasticsearch._types.FieldValue;
import co.elastic.clients.elasticsearch._types.SortOptions;
import co.elastic.clients.elasticsearch._types.SortOrder;
import co.elastic.clients.elasticsearch._types.query_dsl.BoolQuery;
import co.elastic.clients.elasticsearch._types.query_dsl.NestedQuery;
import co.elastic.clients.elasticsearch._types.query_dsl.Query;
import co.elastic.clients.json.JsonData;
import com.google.common.collect.Lists;
import com.jprocms.module.cms.es.dto.*;
import com.jprocms.module.cms.es.enums.EnumContentSort;
import com.jprocms.module.cms.es.enums.EnumQueryType;
import com.jprocms.module.cms.es.enums.EnumSearchMatchType;
import com.jprocms.module.cms.es.enums.EnumSearchPos;
import com.jprocms.module.cms.es.vo.EsContentVO;
import lombok.Data;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @author jprocms
 */
public class EsQueryUtil {

    public static Query generateContentAdminQuery(QueryEntity entity) {
        Assert.notNull(entity);
        switch (entity.queryType) {
            case term:
                return new Query.Builder().term(tq -> tq.field(entity.field)
                        .value(b -> b.stringValue(entity.value.toString()))).build();
            case term_not:
                return new Query.Builder().bool(new BoolQuery.Builder().mustNot(
                        new Query.Builder().term(
                                        m -> m.field(entity.field).value(entity.getValue().toString()))
                                .build()).build()).build();
            case terms:
                List<FieldValue> fieldValues = (List<FieldValue>) ((List) entity.value).stream()
                        .map(s -> FieldValue.of(s.toString())).collect(Collectors.toList());
                return new Query.Builder().terms(
                        tq -> tq.field(entity.field).terms(b -> b.value(fieldValues))).build();
            case terms_not:
                List<FieldValue> fieldValue1s = (List<FieldValue>) ((List) entity.value).stream()
                        .map(s -> FieldValue.of(s.toString())).collect(Collectors.toList());
                return new Query.Builder().bool(new BoolQuery.Builder().mustNot(
                        new Query.Builder().terms(
                                        tq -> tq.field(entity.field).terms(b -> b.value(fieldValue1s)))
                                .build()).build()).build();
            case match:
                return new Query.Builder().match(
                        m -> m.field(entity.field).query(entity.getValue().toString())).build();
            case match_phrase:
                return new Query.Builder().matchPhrase(
                        m -> m.field(entity.field).query(entity.getValue().toString())).build();
            case match_not:
                return new Query.Builder().bool(new BoolQuery.Builder().mustNot(
                        new Query.Builder().match(
                                        m -> m.field(entity.field).query(entity.getValue().toString()))
                                .build()).build()).build();
            case range_gt:
                return new Query.Builder().range(
                        m -> m.field(entity.field).gt(JsonData.of(entity.getValue()))).build();
            case range_lt:
                return new Query.Builder().range(
                        m -> m.field(entity.field).lt(JsonData.of(entity.getValue()))).build();
            case range_gte:
                return new Query.Builder().range(
                        m -> m.field(entity.field).gte(JsonData.of(entity.getValue()))).build();
            case range_lte:
                return new Query.Builder().range(
                        m -> m.field(entity.field).lte(JsonData.of(entity.getValue()))).build();
            case range_from:
                Assert.isTrue(
                        entity.value instanceof Collection || entity.value.getClass().isArray());
                String firstValue;
                String secondValue;
                List list = new ArrayList(2);
                if (entity.value instanceof Collection) {
                    list = new ArrayList((Collection) entity.value);

                } else if (entity.value.getClass().isArray()) {
                    list = Arrays.asList(entity.value);

                }
                Collections.sort(list);
                firstValue = list.get(0).toString();
                secondValue = list.get(1).toString();
                return new Query.Builder().range(
                        m -> m.field(entity.field).from(firstValue).to(secondValue)).build();
            case wildcard:
                return new Query.Builder().wildcard(
                        w -> w.field(entity.field).value("*" + entity.value + "*")).build();
            default:
                return null;
        }
    }

    public static Query generateContentAdminQuery(ContentAdminQueryDto queryDto) {
        if (queryDto == null) {
            return null;
        }
        NestedQuery nestedQuery = generateNestedQuery(queryDto);
        List<Query> queries = generateCommonQuery(queryDto);
        List<String> keyWords = queryDto.getKeyWords();
        if (CollectionUtils.isNotEmpty(keyWords)) {
            final Query query = genarateKeyWordQuery(keyWords, queryDto.getQMathPos(), queryDto.getQMatchType(), false);
            queries.add(query);
        }
        if (nestedQuery != null) {
            queries.add(new Query.Builder().nested(nestedQuery).build());
        }
        return new Query.Builder().bool(new BoolQuery.Builder().must(queries).build()).build();
    }

    private static NestedQuery generateNestedQuery(ContentAdminQueryDto queryDto) {

        Map<String, Object> extendMap = queryDto.getCustoms();
        if (MapUtils.isEmpty(extendMap)) {
            return null;
        }
        List<Query> queries = extendMap.entrySet().stream().map(en -> generateContentAdminQuery(
                        new QueryEntity(getExtendFiled(en.getKey()), EnumQueryType.match, en.getValue())))
                .filter(Objects::nonNull).collect(Collectors.toList());
        NestedQuery nestedQuery = new NestedQuery.Builder().query(
                        q1 -> q1.bool(new BoolQuery.Builder().must(queries).build())).path("extend")
                .build();
        return nestedQuery;
    }

    private static List<Query> generateCommonQuery(ContentAdminQueryDto queryDto) {
        List<QueryEntity> entities = new ArrayList<>();
        if (queryDto.getCreateMode() != null) {
            entities.add(new QueryEntity("createMode", EnumQueryType.term, queryDto.getCreateMode()));
        }
        if (queryDto.getChannelId() != null) {
            entities.add(new QueryEntity("channelId", EnumQueryType.term, queryDto.getChannelId()));
        }
        if (queryDto.getSiteId() != null) {
            entities.add(new QueryEntity("siteId", EnumQueryType.term, queryDto.getSiteId()));
        }
        if (StringUtils.isNotBlank(queryDto.getCreateUser())) {
            entities.add(
                    new QueryEntity("createUser", EnumQueryType.term, queryDto.getCreateUser()));
        }
        if (queryDto.getStatus() != null) {
            entities.add(new QueryEntity("status", EnumQueryType.term, queryDto.getStatus()));
        }
        if (queryDto.getDictBlock() != null) {
            entities.add(new QueryEntity("dictBlock", EnumQueryType.term, queryDto.getDictBlock()));
        }
        if (queryDto.getDictMark() != null) {
            entities.add(new QueryEntity("dictMark", EnumQueryType.term, queryDto.getDictMark()));
        }
        if (StringUtils.isNotBlank(queryDto.getAuthor())) {
            entities.add(new QueryEntity("author", EnumQueryType.match_phrase, queryDto.getAuthor()));
        }
        if (StringUtils.isNotBlank(queryDto.getSource())) {
            entities.add(new QueryEntity("source", EnumQueryType.match_phrase, queryDto.getSource()));
        }
        if (queryDto.getCreateTimeStart() != null) {
            entities.add(new QueryEntity("createTime", EnumQueryType.range_gte,
                    queryDto.getCreateTimeStart()));
        }
        if (queryDto.getCreateTimeEnd() != null) {
            entities.add(new QueryEntity("createTime", EnumQueryType.range_lte,
                    queryDto.getCreateTimeEnd()));
        }
        if (queryDto.getReleaseTimeStart() != null) {
            entities.add(new QueryEntity("releaseTime", EnumQueryType.range_gte,
                    queryDto.getReleaseTimeStart()));
        }
        if (queryDto.getReleaseTimeEnd() != null) {
            entities.add(new QueryEntity("releaseTime", EnumQueryType.range_lte,
                    queryDto.getReleaseTimeEnd()));
        }
        if (queryDto.getExcludeStatus() != null) {
            entities.add(new QueryEntity("status", EnumQueryType.match_not,
                    queryDto.getExcludeStatus()));
        }
        return entities.stream().map(EsQueryUtil::generateContentAdminQuery).collect(Collectors.toList());
    }

    public static QueryPage generateQueryPage(Long pageSize, Long pageNum, Integer sort, Integer offset) {
        if (pageSize == null || pageSize == 0L) {
            pageSize = 20L;
        }
        if (pageNum == null || pageNum == 0L) {
            pageNum = 1L;
        }
        if (sort == null) {
            sort = 0;
        }
        Long from = (pageNum - 1) * pageSize;
        if (offset != null && offset != 0) {
            from = offset.longValue();
        }
        Long size = pageSize;
        EnumContentSort st = EnumContentSort.getContentSort(sort);
        if (st == null) {
            st = EnumContentSort.RELEASE_TIME_DESC;
        }
        List<SortOptions> sortOptions = new ArrayList<>();
        if (st == EnumContentSort.DEFAULT) {
            sortOptions.add(new SortOptions.Builder().field(
                    new FieldSort.Builder().field("topLevel").order(SortOrder.Desc).build()).build());
            //添加默认检索排序优先
            sortOptions.add(new SortOptions.Builder().field(
                    new FieldSort.Builder().field("searchWeight").order(SortOrder.Desc).build()).build());
            sortOptions.add(new SortOptions.Builder().field(
                    new FieldSort.Builder().field("releaseTime").order(SortOrder.Desc).build()).build());
            sortOptions.add(new SortOptions.Builder().field(
                    new FieldSort.Builder().field("id").order(SortOrder.Desc).build()).build());
        } else {
            //添加默认检索排序优先
            sortOptions.add(new SortOptions.Builder().field(
                    new FieldSort.Builder().field("searchWeight").order(SortOrder.Desc).build()).build());
            sortOptions.add(new SortOptions.Builder().field(
                    new FieldSort.Builder().field(st.getField())
                            .order(st.isAsc() ? SortOrder.Asc : SortOrder.Desc).build()).build());
        }
        return new QueryPage(pageNum.intValue(), from.intValue(), size.intValue(), sortOptions);
    }

    public static Query generateContentAdminQuery(EsFrontSearchDto dto, List<Long> notAllowSearchChannelIds) {
        if (dto == null) {
            return null;
        }
        List<Query> queries = generateQueryList(dto, notAllowSearchChannelIds, dto.isDocSearchSupport());
        return new Query.Builder().bool(new BoolQuery.Builder().must(queries).build()).build();
    }

    public static Query generateContentAdminQuery(ContentSearchQueryDto dto) {
        if (dto == null) {
            return null;
        }
        List<Query> ans = new ArrayList<>();
        String word = dto.getQ();
        if (StringUtils.isNotEmpty(word)) {
            ans.add(genarateWordQuery(word));
        }
        List<QueryEntity> entities = new ArrayList<>(10);
        if (dto.getChannelId() != null) {
            entities.add(new QueryEntity("channelId", EnumQueryType.term, dto.getChannelId()));
        }
        if (dto.getSiteId() != null) {
            entities.add(new QueryEntity("siteId", EnumQueryType.term, dto.getSiteId()));
        }
        if (dto.getReleaseTimeStart() != null) {
            entities.add(new QueryEntity("releaseTime", EnumQueryType.range_gte,
                    dto.getReleaseTimeStart()));
        }
        if (dto.getReleaseTimeEnd() != null) {
            entities.add(new QueryEntity("releaseTime", EnumQueryType.range_lte,
                    dto.getReleaseTimeEnd()));
        }
        if (dto.getStatus() != null) {
            entities.add(new QueryEntity("status", EnumQueryType.term, dto.getStatus()));
        }
        ans.addAll(entities.stream().map(EsQueryUtil::generateContentAdminQuery).collect(Collectors.toList()));
        return new Query.Builder().bool(new BoolQuery.Builder().must(ans).build()).build();
    }


    private static List<Query> generateQueryList(EsFrontSearchDto dto, List<Long> notAllowSearchChannelIds, boolean docSearchSupport) {
        List<Query> queries = new ArrayList<>();
        List<String> keyWords = dto.getKeyWords();
        if (CollectionUtils.isNotEmpty(keyWords)) {
            queries.add(genarateKeyWordQuery(keyWords, dto.getQMathPos(), dto.getQMatchType(), docSearchSupport));
        }
        List<QueryEntity> queryEntities = new ArrayList<>(10);
        if (dto.getTopLevel() != null) {
            queryEntities.add(new QueryEntity("topLevel", EnumQueryType.term, dto.getTopLevel()));
        }
        if (dto.getSiteId() != null) {
            queryEntities.add(new QueryEntity("siteId", EnumQueryType.term, dto.getSiteId()));
        }
        if (dto.getReleaseTimeStart() != null) {
            queryEntities.add(new QueryEntity("releaseTime", EnumQueryType.range_gte,
                    dto.getReleaseTimeStart()));
        }
        if (dto.getReleaseTimeEnd() != null) {
            queryEntities.add(new QueryEntity("releaseTime", EnumQueryType.range_lte,
                    dto.getReleaseTimeEnd()));
        }
        if (CollectionUtils.isNotEmpty(notAllowSearchChannelIds)) {
            queryEntities.add(new QueryEntity("channelId", EnumQueryType.terms_not, notAllowSearchChannelIds));
        }
        if (dto.getCreateTimeStart() != null) {
            queryEntities.add(new QueryEntity("createTime", EnumQueryType.range_gte,
                    dto.getCreateTimeStart()));
        }
        if (dto.getCreateTimeEnd() != null) {
            queryEntities.add(
                    new QueryEntity("createTime", EnumQueryType.range_lte, dto.getCreateTimeEnd()));
        }
        if (CollectionUtils.isNotEmpty(dto.getModelIds())) {
            queryEntities.add(new QueryEntity("modelId", EnumQueryType.terms, dto.getModelIds()));
        }
        if (CollectionUtils.isNotEmpty(dto.getDictBlocks())) {
            queryEntities.add(new QueryEntity("dictBlock", EnumQueryType.terms, dto.getDictBlocks()));
        }
        if (CollectionUtils.isNotEmpty(dto.getExcludeDictBlocks())) {
            queryEntities.add(
                    new QueryEntity("dictBlock", EnumQueryType.terms_not, dto.getExcludeDictBlocks()));
        }
        if (CollectionUtils.isNotEmpty(dto.getDictMarks())) {
            queryEntities.add(new QueryEntity("dictMark", EnumQueryType.terms, dto.getDictMarks()));
        }
        if (CollectionUtils.isNotEmpty(dto.getExcludeDictMarks())) {
            queryEntities.add(new QueryEntity("dictMark", EnumQueryType.terms_not, dto.getExcludeDictMarks()));
        }
        if (CollectionUtils.isNotEmpty(dto.getTagIds())) {
            queryEntities.add(new QueryEntity("tagIds", EnumQueryType.terms, dto.getTagIds()));
        }
        if (dto.getStatus() != null) {
            queryEntities.add(new QueryEntity("status", EnumQueryType.term, dto.getStatus()));
        }
        if (StringUtils.isNotBlank(dto.getTitle())) {
            queryEntities.add(new QueryEntity("title", EnumQueryType.match_phrase, dto.getTitle()));
        }
        if (StringUtils.isNotBlank(dto.getAuthor())) {
            queryEntities.add(new QueryEntity("author", EnumQueryType.match_phrase, dto.getAuthor()));
        }
        if (StringUtils.isNotBlank(dto.getSource())) {
            queryEntities.add(new QueryEntity("source", EnumQueryType.match_phrase, dto.getSource()));
        }
        queries.addAll(queryEntities.stream().map(EsQueryUtil::generateContentAdminQuery).collect(Collectors.toList()));
        if (CollectionUtils.isNotEmpty(dto.getExcludeIds())) {
            QueryEntity entity = new QueryEntity("id", EnumQueryType.terms, dto.getExcludeIds());
            queries.add(new Query.Builder().bool(new BoolQuery.Builder().mustNot(generateContentAdminQuery(entity)).build()).build());
        }

        if (CollectionUtils.isNotEmpty(dto.getChannelIds())) {
            QueryEntity entity = new QueryEntity("channelId", EnumQueryType.terms, dto.getChannelIds());
            QueryEntity entity2 = new QueryEntity("subChannelIds", EnumQueryType.terms, dto.getChannelIds());
            queries.add(new Query.Builder().bool(new BoolQuery.Builder().should(generateContentAdminQuery(entity), generateContentAdminQuery(entity2)).build()).build());
        }

        if (MapUtils.isNotEmpty(dto.getCustoms())) {
            final NestedQuery nestedQuery = generateNestedQuery(dto.getCustoms());
            if (nestedQuery != null) {
                final Query query = new Query.Builder().nested(nestedQuery).build();
                queries.add(query);
            }
        }
        return queries;
    }

    private static NestedQuery generateNestedQuery(Map<String, Object> map) {
        if (MapUtils.isEmpty(map)) {
            return null;
        }
        List<Query> queries = map.entrySet().stream().map(en -> generateContentAdminQuery(
                new QueryEntity(getExtendFiled(en.getKey()), EnumQueryType.term,
                        en.getValue()))).filter(Objects::nonNull).collect(Collectors.toList());
        NestedQuery nestedQuery = new NestedQuery.Builder().query(
                        q1 -> q1.bool(new BoolQuery.Builder().must(queries).build())).path("customs")
                .build();
        return nestedQuery;
    }

    private static Query genarateKeyWordQuery(List<String> keyWords, Integer keyWordField,
                                              Integer keyWordMatchType, boolean docSearchSupport) {
        Query titleQuery = null;
        Query txtQuery = null;
        Query docQuery = null;
        if (Objects.equals(EnumSearchPos.TITLE.getVal(), keyWordField)) {
            return genarateTitleQuery(keyWords, keyWordMatchType);
        } else if (Objects.equals(EnumSearchPos.TEXT.getVal(), keyWordField)) {
            return genarateTxtQuery(keyWords, keyWordMatchType);
        } else if (Objects.equals(EnumSearchPos.AUTHOR.getVal(), keyWordField)) {
            return genarateQuery(keyWords, keyWordMatchType, "author");
        } else if (Objects.equals(EnumSearchPos.SOURCE.getVal(), keyWordField)) {
            return genarateQuery(keyWords, keyWordMatchType, "source");
        } else {
            titleQuery = genarateTitleQuery(keyWords, keyWordMatchType);
            txtQuery = genarateTxtQuery(keyWords, keyWordMatchType);
            if (docSearchSupport) {
                docQuery = genarateDocQuery(keyWords, keyWordMatchType);
                return new Query.Builder().bool((new BoolQuery.Builder().should(titleQuery, txtQuery, docQuery).build())).build();
            }
            return new Query.Builder().bool((new BoolQuery.Builder().should(titleQuery, txtQuery).build())).build();
        }
    }

    private static Query genarateTxtQuery(List<String> keyWords, Integer keyWordMatchType) {
        Query query = genarateQuery(keyWords, keyWordMatchType, "txts.txt");
        NestedQuery nestedQuery = new NestedQuery.Builder().query(query).path("txts").build();
        final Query.Builder builder = new Query.Builder();
        return builder.nested(nestedQuery).build();
    }

    private static Query genarateDocQuery(List<String> keyWords, Integer keyWordMatchType) {
        Query query = genarateQuery(keyWords, keyWordMatchType, "docs.txt");
        NestedQuery nestedQuery = new NestedQuery.Builder().query(query).path("docs").build();
        final Query.Builder builder = new Query.Builder();
        return builder.nested(nestedQuery).build();
    }

    private static Query genarateTitleQuery(List<String> keyWords, Integer keyWordMatchType) {
        return genarateQuery(keyWords, keyWordMatchType, "title");

    }

    private static Query genarateQuery(List<String> keyWords, Integer keyWordMatchType,
                                       String field) {

        if (Objects.equals(EnumSearchMatchType.SHOULD.getVal(), keyWordMatchType)) {
            List<Query> queries = keyWords.stream().map(keyWord -> {
                /**
                 * 分词效果可能达不到用户想要的 加上模糊匹配搜索
                 */
                QueryEntity entity = new QueryEntity(field, EnumQueryType.match_phrase, keyWord);
                QueryEntity entity1 = new QueryEntity(field, EnumQueryType.wildcard, keyWord);
                return new Query.Builder().bool(new BoolQuery.Builder().should(
                                Lists.newArrayList(generateContentAdminQuery(entity), generateContentAdminQuery(entity1))).build())
                        .build();
            }).collect(Collectors.toList());
            return new Query.Builder().bool(new BoolQuery.Builder().should(queries).build())
                    .build();
        } else if (Objects.equals(EnumSearchMatchType.MAST.getVal(), keyWordMatchType)) {
            List<Query> queries = keyWords.stream().map(keyWord -> {
                QueryEntity entity = new QueryEntity(field, EnumQueryType.match_phrase, keyWord);
                QueryEntity entity1 = new QueryEntity(field, EnumQueryType.wildcard, keyWord);
                return new Query.Builder().bool(new BoolQuery.Builder().should(
                                Lists.newArrayList(generateContentAdminQuery(entity), generateContentAdminQuery(entity1))).build())
                        .build();
            }).collect(Collectors.toList());
            return new Query.Builder().bool(new BoolQuery.Builder().must(queries).build()).build();
        } else {
            List<Query> queries = keyWords.stream().map(keyWord -> {
                QueryEntity entity = new QueryEntity(field, EnumQueryType.match_phrase, keyWord);
                QueryEntity entity1 = new QueryEntity(field, EnumQueryType.wildcard, keyWord);
                return new Query.Builder().bool(new BoolQuery.Builder().should(
                                Lists.newArrayList(generateContentAdminQuery(entity), generateContentAdminQuery(entity1))).build())
                        .build();
            }).collect(Collectors.toList());
            return new Query.Builder().bool(new BoolQuery.Builder().mustNot(queries).build())
                    .build();
        }

    }

    public static Query generateContentAdminQuery(EsRelationContentQueryDto dto) {
        if (dto == null) {
            return null;
        }
        List<Query> queries = new ArrayList<>();
        Query query = generateQueryWithIdsAndTagIds(dto.getIds(), dto.getTagIds());
        if (query == null) {
            return null;
        }
        queries.add(query);
        if (dto.getStatus() != null) {
            queries.add(
                    generateContentAdminQuery(new QueryEntity("status", EnumQueryType.term, dto.getStatus())));
        }
        return new Query.Builder().bool(new BoolQuery.Builder().must(queries).build()).build();
    }

    private static Query generateQueryWithIdsAndTagIds(List<Long> ids, List<Long> tagIds) {
        List<QueryEntity> entities = new ArrayList<>(2);
        if (CollectionUtils.isNotEmpty(tagIds)) {
            entities.add(new QueryEntity("tagIds", EnumQueryType.terms, tagIds));
        }
        if (CollectionUtils.isNotEmpty(ids)) {
            entities.add(new QueryEntity("id", EnumQueryType.terms, ids));
        }
        List<Query> queries = entities.stream().map(EsQueryUtil::generateContentAdminQuery)
                .collect(Collectors.toList());
        if (queries.size() > 1) {
            return new Query.Builder().bool((new BoolQuery.Builder().should(queries).build()))
                    .build();
        }
        return queries.get(0);
    }

    public static Query generateContentAdminQuery(EsNextContentDto dto, EsContentVO esContentVO) {
        if (dto == null || esContentVO == null) {
            return null;
        }
        Query nextQuery = genarateNextQuery(esContentVO, dto.getIsNext());
        List<Query> queries = genarateQuerys(dto);
        if (nextQuery != null) {
            queries.add(nextQuery);
        }
        queries.add(generateContentAdminQuery(new QueryEntity("id", EnumQueryType.term_not, esContentVO.getId())));
        return new Query.Builder().bool(new BoolQuery.Builder().must(queries).build()).build();
    }

    private static List<Query> genarateQuerys(EsNextContentDto dto) {
        List<QueryEntity> entities = new ArrayList<>(2);
        if (dto.getSiteId() != null) {
            entities.add(new QueryEntity("siteId", EnumQueryType.term, dto.getSiteId()));
        }
        if (dto.getStatus() != null) {
            entities.add(new QueryEntity("status", EnumQueryType.term, dto.getStatus()));
        }
        if (dto.getChannelId() != null) {
            entities.add(new QueryEntity("channelId", EnumQueryType.term, dto.getChannelId()));
        }
        return entities.stream().map(EsQueryUtil::generateContentAdminQuery).collect(Collectors.toList());
    }

    private static Query genarateNextQuery(EsContentVO esContentVO, Boolean isNext) {
        Query query = null;
        EnumQueryType range_gt = EnumQueryType.range_gt;
        EnumQueryType rangeType = range_gt;
        if (Boolean.TRUE.equals(isNext)) {
            rangeType = EnumQueryType.range_lt;
        }
        Query query1 = generateContentAdminQuery(new QueryEntity("topLevel", rangeType, esContentVO.getTopLevel()));


        List<QueryEntity> queryEntities = new ArrayList<>(2);
        queryEntities.add(new QueryEntity("topLevel", EnumQueryType.term, esContentVO.getTopLevel()));
        queryEntities.add(new QueryEntity("releaseTime", rangeType, esContentVO.getReleaseTime().getTime()));
        List<Query> queries = queryEntities.stream().map(EsQueryUtil::generateContentAdminQuery).collect(Collectors.toList());
        Query query2 = new Query.Builder().bool(new BoolQuery.Builder().must(queries).build()).build();


        List<QueryEntity> queryEntities3 = new ArrayList<>(3);
        queryEntities3.add(new QueryEntity("topLevel", EnumQueryType.term, esContentVO.getTopLevel()));
        queryEntities3.add(new QueryEntity("releaseTime", EnumQueryType.term, esContentVO.getReleaseTime().getTime()));
        queryEntities3.add(new QueryEntity("id", rangeType, esContentVO.getId()));
        List<Query> queries3 = queryEntities3.stream().map(EsQueryUtil::generateContentAdminQuery).collect(Collectors.toList());
        Query query3 = new Query.Builder().bool(new BoolQuery.Builder().must(queries3).build()).build();

        query = new Query.Builder().bool(new BoolQuery.Builder().should(query2, query1, query3).build()).build();

        return query;
    }

    private static Query genarateWordQuery(String word) {
        Query titleQuery = generateContentAdminQuery(
                new QueryEntity("title", EnumQueryType.match_phrase, word));
        Query query = generateContentAdminQuery(new QueryEntity("txts.txt", EnumQueryType.match_phrase, word));
        NestedQuery nestedQuery = new NestedQuery.Builder().query(query).path("txt").build();
        Query txtQuery = new Query.Builder().nested(nestedQuery).build();
        Query authorQuery = generateContentAdminQuery(
                new QueryEntity("author", EnumQueryType.match_phrase, word));
        return new Query.Builder().bool(
                        (new BoolQuery.Builder().should(titleQuery, txtQuery, authorQuery).build()))
                .build();
    }

    public static QueryPage generateNextQueryPage(Long pageSize, Long pageNum, Boolean isNext, Integer topLevel) {
        if (pageSize == null || pageSize == 0L) {
            pageSize = 1L;
        }
        if (pageNum == null || pageNum == 0L) {
            pageNum = 1L;
        }
        Long from = (pageNum - 1) * pageSize;
        Long size = pageSize;
        List<SortOptions> sortOptions = new ArrayList<>();
        SortOrder topSortOrder = !isNext ? SortOrder.Asc : SortOrder.Desc;
        SortOrder releaseTimeSortOrder = !isNext ? SortOrder.Asc : SortOrder.Desc;
        SortOrder idSortOrder = !isNext ? SortOrder.Asc : SortOrder.Desc;
        if (topLevel > 0) {
            sortOptions.add(new SortOptions.Builder().field(new FieldSort.Builder().field("topLevel").order(topSortOrder).build()).build());
        }
        //添加默认检索排序优先
        sortOptions.add(new SortOptions.Builder().field(new FieldSort.Builder().field("searchWeight").order(SortOrder.Desc).build()).build());
        sortOptions.add(new SortOptions.Builder().field(new FieldSort.Builder().field("releaseTime").order(releaseTimeSortOrder).build()).build());
        sortOptions.add(new SortOptions.Builder().field(new FieldSort.Builder().field("id").order(idSortOrder).build()).build());
        return new QueryPage(pageNum.intValue(), from.intValue(), size.intValue(), sortOptions);
    }

    private static SortOrder reverseOrder(SortOrder sortOrder) {
        return sortOrder == SortOrder.Asc ? SortOrder.Desc : SortOrder.Asc;
    }

    @Data
    public static class QueryEntity {
        private String field;
        private EnumQueryType queryType;
        private Object value;

        public QueryEntity(String field, EnumQueryType queryType, Object value) {
            Assert.notNull(field);
            Assert.notNull(queryType);
            Assert.notNull(value);
            this.field = field;
            this.queryType = queryType;
            this.value = value;
        }
    }

    public static String getExtendFiled(String field) {
        return "customs." + field;
    }

    @Data
    public static class QueryPage {
        private Integer pageNum;
        private Integer from;
        private Integer size;
        private List<SortOptions> sortOptions;

        public QueryPage(Integer pageNum, Integer from, Integer to, List<SortOptions> sortOptions) {
            Assert.notNull(from);
            Assert.notNull(to);
            Assert.notNull(sortOptions);
            Assert.notNull(pageNum);
            this.pageNum = pageNum;
            this.from = from;
            this.size = to;
            this.sortOptions = sortOptions;
        }

        public QueryPage() {
        }
    }
}
